﻿using System;
using System.Linq;
using DotNetNeural.Data.Learning;
using DotNetNeural.Data.Utils;
using DotNetNeural.Utils;

namespace DotNetNeural.Research
{
    public class TrainingSetGenerator
    {
        public const float DispersionFromClusterCenter = 25;
        public const float MinComponentValue = 0;
        public const float MaxComponentValue = 100;

        public ITrainingSet CreateTrainingSet(TrainingSetSettings settings)
        {
            if (settings == null)
                throw new NullReferenceException("Illegal null-reference settings");

            //each cluster has maximum component value at cluster index. Other components are zero
            TrainingSet set = new TrainingSet(settings.InputsCount, settings.ClustersCount);

            VectorGenerator vGen = new VectorGenerator();
            var clusterCenters =
                vGen.GenereateDistant(
                    settings.ClustersCount, 
                    settings.DistanceBetweenClusters, 
                    settings.InputsCount, 
                    MinComponentValue, 
                    MaxComponentValue
                );

            ClusterGenerator cGen = new ClusterGenerator();

            int clusterIndex = 0;

            foreach (var cc in clusterCenters)
            {
                int itemsCountInCluster = GetItemsCountInCluster(settings);

                var cluster = cGen.Generate(cc, itemsCountInCluster, DispersionFromClusterCenter);
                int controlItemsCount = (int)(settings.ControlItemsInClusterRatio * itemsCountInCluster);
                float[] clusterOutput = new float[settings.ClustersCount];
                clusterOutput.SetForEach(0f);
                clusterOutput[clusterIndex] = 0.99f;

                var learningItems = cluster.Take(itemsCountInCluster - controlItemsCount);
                var controlItems = cluster.Skip(itemsCountInCluster - controlItemsCount).Take(controlItemsCount);

                foreach (var i in learningItems)
                {
                    set.AddItem(new TrainingSetItem(i, clusterOutput), false);
                }

                foreach (var i in controlItems)
                {
                    set.AddItem(new TrainingSetItem(i, clusterOutput), true);
                }

                ++clusterIndex;
            }

            return set;
        }

        private int GetItemsCountInCluster(TrainingSetSettings settings)
        {
            Random rnd = new Random();
            return rnd.Next(settings.MinItemsInCluster, settings.MaxItemsInCluster + 1);
        }
    }
}
